home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 2.iso / STUTTGART / UNIXTOOL / MEMACS / C / Line < prev    next >
Text File  |  1990-06-26  |  21KB  |  865 lines

  1. /*
  2.  * The functions in this file are a general set of line management utilities.
  3.  * They are the only routines that touch the text. They also touch the buffer
  4.  * and window structures, to make sure that the necessary updating gets done.
  5.  * There are routines in this file that handle the kill buffer too. It isn't
  6.  * here for any good reason.
  7.  *
  8.  * Note that this code only updates the dot and mark values in the window list.
  9.  * Since all the code acts on the current window, the buffer that we are
  10.  * editing must be being displayed, which means that "b_nwnd" is non zero,
  11.  * which means that the dot and mark values in the buffer headers are nonsense.
  12.  */
  13.  
  14. #include    <stdio.h>
  15. #include    "estruct.h"
  16. #include    "eproto.h"
  17. #include    "edef.h"
  18. #include    "elang.h"
  19.  
  20. #define    BSIZE(a)    (a + NBLOCK - 1) & (~(NBLOCK - 1))
  21.  
  22. /*
  23.  * This routine allocates a block of memory large enough to hold a LINE
  24.  * containing "used" characters. Return a pointer to the new block, or
  25.  * NULL if there isn't any memory left. Print a message in the message
  26.  * line if no space.
  27.  */
  28.  
  29. LINE *PASCAL NEAR lalloc(used)
  30.  
  31. register int used;
  32.  
  33. {
  34.     register LINE    *lp;
  35.  
  36.     if ((lp = (LINE *)malloc(sizeof(LINE)+used)) == NULL) {
  37.         mlwrite(TEXT94);
  38. /*                      "%%Out of memory" */
  39.         return(NULL);
  40.     }
  41.     lp->l_size = used;
  42.     lp->l_used = used;
  43.     return(lp);
  44. }
  45.  
  46. /*
  47.  * Delete line "lp". Fix all of the links that might point at it (they are
  48.  * moved to offset 0 of the next line. Unlink the line from whatever buffer it
  49.  * might be in. Release the memory. The buffers are updated too; the magic
  50.  * conditions described in the above comments don't hold here.
  51.  */
  52. PASCAL NEAR lfree(lp)
  53. register LINE    *lp;
  54. {
  55.     register BUFFER *bp;
  56.     SCREEN *scrp;        /* screen to fix pointers in */
  57.     register WINDOW *wp;
  58.     register int cmark;        /* current mark */
  59.  
  60.     /* in all screens.... */
  61.     scrp = first_screen;
  62.     while (scrp) {
  63.         wp = scrp->s_first_window;
  64.         while (wp != NULL) {
  65.             if (wp->w_linep == lp)
  66.                 wp->w_linep = lp->l_fp;
  67.             if (wp->w_dotp    == lp) {
  68.                 wp->w_dotp  = lp->l_fp;
  69.                 wp->w_doto  = 0;
  70.             }
  71.             for (cmark = 0; cmark < NMARKS; cmark++) {
  72.                 if (wp->w_markp[cmark] == lp) {
  73.                     wp->w_markp[cmark] = lp->l_fp;
  74.                     wp->w_marko[cmark] = 0;
  75.                 }
  76.             }
  77.             wp = wp->w_wndp;
  78.         }
  79.  
  80.         /* next screen! */
  81.         scrp = scrp->s_next_screen;
  82.     }
  83.  
  84.     bp = bheadp;
  85.     while (bp != NULL) {
  86.         if (bp->b_nwnd == 0) {
  87.             if (bp->b_dotp    == lp) {
  88.                 bp->b_dotp = lp->l_fp;
  89.                 bp->b_doto = 0;
  90.             }
  91.             for (cmark = 0; cmark < NMARKS; cmark++) {
  92.                 if (bp->b_markp[cmark] == lp) {
  93.                     bp->b_markp[cmark] = lp->l_fp;
  94.                     bp->b_marko[cmark] = 0;
  95.                 }
  96.             }
  97.         }
  98.         bp = bp->b_bufp;
  99.     }
  100.     lp->l_bp->l_fp = lp->l_fp;
  101.     lp->l_fp->l_bp = lp->l_bp;
  102.     free((char *) lp);
  103. }
  104.  
  105. /*
  106.  * This routine gets called when a character is changed in place in the current
  107.  * buffer. It updates all of the required flags in the buffer and window
  108.  * system. The flag used is passed as an argument; if the buffer is being
  109.  * displayed in more than 1 window we change EDIT t HARD. Set MODE if the
  110.  * mode line needs to be updated (the "*" has to be set).
  111.  */
  112. PASCAL NEAR lchange(flag)
  113. register int    flag;
  114. {
  115.     register WINDOW *wp;
  116.     SCREEN *scrp;        /* screen to fix pointers in */
  117.  
  118.     if (curbp->b_nwnd != 1)         /* Ensure hard.     */
  119.         flag = WFHARD;
  120.     if ((curbp->b_flag&BFCHG) == 0) {    /* First change, so    */
  121.         flag |= WFMODE;         /* update mode lines.    */
  122.         curbp->b_flag |= BFCHG;
  123.     }
  124.  
  125.     /* in all screens.... */
  126.     scrp = first_screen;
  127.     while (scrp) {
  128.         /* make sure all the needed windows get this flag */
  129.         wp = scrp->s_first_window;
  130.         while (wp != NULL) {
  131.             if (wp->w_bufp == curbp)
  132.                 wp->w_flag |= flag;
  133.             wp = wp->w_wndp;
  134.         }
  135.  
  136.         /* next screen! */
  137.         scrp = scrp->s_next_screen;
  138.     }
  139. }
  140.  
  141. PASCAL NEAR insspace(f, n)    /* insert spaces forward into text */
  142.  
  143. int f, n;    /* default flag and numeric argument */
  144.  
  145. {
  146.     linsert(n, ' ');
  147.     backchar(f, n);
  148. }
  149.  
  150. /*
  151.  * linstr -- Insert a string at the current point
  152.  */
  153.  
  154. PASCAL NEAR linstr(instr)
  155. char    *instr;
  156. {
  157.     register int status;
  158.  
  159.     status = TRUE;
  160.     if (instr != NULL)
  161.         while (*instr) {
  162.             status = ((*instr == '\r') ? lnewline(): linsert(1, *instr));
  163.  
  164.             /* Insertion error? */
  165.             if (status != TRUE) {
  166.                 mlwrite(TEXT168);
  167. /*                                      "%%Can not insert string" */
  168.                 break;
  169.             }
  170.             instr++;
  171.         }
  172.     return(status);
  173. }
  174.  
  175. /*
  176.  * Insert "n" copies of the character "c" at the current location of dot. In
  177.  * the easy case all that happens is the text is stored in the line. In the
  178.  * hard case, the line has to be reallocated. When the window list is updated,
  179.  * take special care; I screwed it up once. You always update dot in the
  180.  * current window. You update mark, and a dot in another window, if it is
  181.  * greater than the place where you did the insert. Return TRUE if all is
  182.  * well, and FALSE on errors.
  183.  */
  184.  
  185. #if    PROTO
  186. PASCAL NEAR linsert(int n, char c)
  187. #else
  188. PASCAL NEAR linsert(n, c)
  189.  
  190. int    n;
  191. char    c;
  192. #endif
  193.  
  194. {
  195.     register char    *cp1;
  196.     register char    *cp2;
  197.     register LINE    *lp1;
  198.     register LINE    *lp2;
  199.     register LINE    *lp3;
  200.     register int    doto;
  201.     register int    i;
  202.     register WINDOW *wp;
  203.     SCREEN *scrp;        /* screen to fix pointers in */
  204.     int cmark;        /* current mark */
  205.  
  206.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  207.         return(rdonly());    /* we are in read only mode    */
  208.  
  209.     /* a zero insert means do nothing! */
  210.     if (n == 0)
  211.         return(TRUE);
  212.  
  213.     /* Negative numbers of inserted characters are right out! */
  214.     if (n < 1)
  215.         return(FALSE);
  216.  
  217.     /* mark the current window's buffer as changed */
  218.     lchange(WFEDIT);
  219.  
  220.     lp1 = curwp->w_dotp;            /* Current line     */
  221.     if (lp1 == curbp->b_linep) {        /* At the end: special    */
  222.         if (curwp->w_doto != 0) {
  223.             mlwrite(TEXT170);
  224. /*                              "bug: linsert" */
  225.             return(FALSE);
  226.         }
  227.         if ((lp2=lalloc(BSIZE(n))) == NULL)    /* Allocate new line    */
  228.             return(FALSE);
  229.         lp2->l_used = n;
  230.         lp3 = lp1->l_bp;        /* Previous line    */
  231.         lp3->l_fp = lp2;        /* Link in        */
  232.         lp2->l_fp = lp1;
  233.         lp1->l_bp = lp2;
  234.         lp2->l_bp = lp3;
  235.         for (i=0; i<n; ++i)
  236.             lp2->l_text[i] = c;
  237.         curwp->w_dotp = lp2;
  238.         curwp->w_doto = n;
  239.         return(TRUE);
  240.     }
  241.     doto = curwp->w_doto;            /* Save for later.    */
  242.     if (lp1->l_used+n > lp1->l_size) {    /* Hard: reallocate    */
  243.         if ((lp2=lalloc(BSIZE(lp1->l_used+n))) == NULL)
  244.             return(FALSE);
  245.         lp2->l_used = lp1->l_used+n;
  246.         cp1 = &lp1->l_text[0];
  247.         cp2 = &lp2->l_text[0];
  248.         while (cp1 != &lp1->l_text[doto])
  249.             *cp2++ = *cp1++;
  250.         cp2 += n;
  251.         while (cp1 != &lp1->l_text[lp1->l_used])
  252.             *cp2++ = *cp1++;
  253.         lp1->l_bp->l_fp = lp2;
  254.         lp2->l_fp = lp1->l_fp;
  255.         lp1->l_fp->l_bp = lp2;
  256.         lp2->l_bp = lp1->l_bp;
  257.         free((char *) lp1);
  258.     } else {                /* Easy: in place    */
  259.         lp2 = lp1;            /* Pretend new line    */
  260.         lp2->l_used += n;
  261.         cp2 = &lp1->l_text[lp1->l_used];
  262.         cp1 = cp2-n;
  263.         while (cp1 != &lp1->l_text[doto])
  264.             *--cp2 = *--cp1;
  265.     }
  266.     for (i=0; i<n; ++i)            /* Add the characters    */
  267.         lp2->l_text[doto+i] = c;
  268.     /* in all screens.... */
  269.     scrp = first_screen;
  270.     while (scrp) {
  271.  
  272.         wp = scrp->s_first_window;
  273.         while (wp != NULL) {
  274.             if (wp->w_linep == lp1)
  275.                 wp->w_linep = lp2;
  276.             if (wp->w_dotp == lp1) {
  277.                 wp->w_dotp = lp2;
  278.                 if (wp==curwp || wp->w_doto>doto)
  279.                     wp->w_doto += n;
  280.             }
  281.             for (cmark = 0; cmark < NMARKS; cmark++) {
  282.                 if (wp->w_markp[cmark] == lp1) {
  283.                     wp->w_markp[cmark] = lp2;
  284.                     if (wp->w_marko[cmark] > doto)
  285.                         wp->w_marko[cmark] += n;
  286.                 }
  287.             }
  288.             wp = wp->w_wndp;
  289.         }
  290.  
  291.         /* next screen! */
  292.         scrp = scrp->s_next_screen;
  293.     }
  294.     return(TRUE);
  295. }
  296.  
  297. /*
  298.  * Overwrite a character into the current line at the current position
  299.  *
  300.  */
  301.  
  302. #if    PROTO
  303. PASCAL NEAR lowrite(char c)
  304. #else
  305. PASCAL NEAR lowrite(c)
  306.  
  307. char c;        /* character to overwrite on current position */
  308. #endif
  309.  
  310. {
  311.     if (curwp->w_doto < curwp->w_dotp->l_used &&
  312.         (lgetc(curwp->w_dotp, curwp->w_doto) != '\t' ||
  313.          (curwp->w_doto) % 8 == 7))
  314.             ldelete(1L, FALSE);
  315.     return(linsert(1, c));
  316. }
  317.  
  318. /*
  319.  * lover -- Overwrite a string at the current point
  320.  */
  321.  
  322. PASCAL NEAR lover(ostr)
  323.  
  324. char    *ostr;
  325.  
  326. {
  327.     register int status = TRUE;
  328.  
  329.     if (ostr != NULL)
  330.         while (*ostr && status == TRUE) {
  331.             status = ((*ostr == '\r') ? lnewline(): lowrite(*ostr));
  332.  
  333.             /* Insertion error? */
  334.             if (status != TRUE) {
  335.                 mlwrite(TEXT172);
  336. /*                                      "%%Out of memory while overwriting" */
  337.                 break;
  338.             }
  339.             ostr++;
  340.         }
  341.     return(status);
  342. }
  343.  
  344. /*
  345.  * Insert a newline into the buffer at the current location of dot in the
  346.  * current window. The funny ass-backwards way it does things is not a botch;
  347.  * it just makes the last line in the file not a special case. Return TRUE if
  348.  * everything works out and FALSE on error (memory allocation failure). The
  349.  * update of dot and mark is a bit easier then in the above case, because the
  350.  * split forces more updating.
  351.  */
  352. PASCAL NEAR lnewline()
  353. {
  354.     register char    *cp1;
  355.     register char    *cp2;
  356.     register LINE    *lp1;
  357.     register LINE    *lp2;
  358.     register int    doto;
  359.     register WINDOW *wp;
  360.     SCREEN *scrp;        /* screen to fix pointers in */
  361.     int cmark;        /* current mark */
  362.  
  363.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  364.         return(rdonly());    /* we are in read only mode    */
  365.     lchange(WFHARD);
  366.     lp1  = curwp->w_dotp;            /* Get the address and    */
  367.     doto = curwp->w_doto;            /* offset of "."    */
  368.     if ((lp2=lalloc(doto)) == NULL)     /* New first half line    */
  369.         return(FALSE);
  370.     cp1 = &lp1->l_text[0];            /* Shuffle text around    */
  371.     cp2 = &lp2->l_text[0];
  372.     while (cp1 != &lp1->l_text[doto])
  373.         *cp2++ = *cp1++;
  374.     cp2 = &lp1->l_text[0];
  375.     while (cp1 != &lp1->l_text[lp1->l_used])
  376.         *cp2++ = *cp1++;
  377.     lp1->l_used -= doto;
  378.     lp2->l_bp = lp1->l_bp;
  379.     lp1->l_bp = lp2;
  380.     lp2->l_bp->l_fp = lp2;
  381.     lp2->l_fp = lp1;
  382.  
  383.     /* in all screens.... */
  384.     scrp = first_screen;
  385.     while (scrp) {
  386.  
  387.         wp = scrp->s_first_window;
  388.         while (wp != NULL) {
  389.             if (wp->w_linep == lp1)
  390.                 wp->w_linep = lp2;
  391.             if (wp->w_dotp == lp1) {
  392.                 if (wp->w_doto < doto)
  393.                     wp->w_dotp = lp2;
  394.                 else
  395.                     wp->w_doto -= doto;
  396.             }
  397.             for (cmark = 0; cmark < NMARKS; cmark++) {
  398.                 if (wp->w_markp[cmark] == lp1) {
  399.                     if (wp->w_marko[cmark] < doto)
  400.                         wp->w_markp[cmark] = lp2;
  401.                     else
  402.                         wp->w_marko[cmark] -= doto;
  403.                 }
  404.             }
  405.             wp = wp->w_wndp;
  406.         }
  407.  
  408.         /* next screen! */
  409.         scrp = scrp->s_next_screen;
  410.     }
  411.     return(TRUE);
  412. }
  413.  
  414. /*
  415.  * This function deletes "n" bytes, starting at dot. It understands how to
  416.  * deal with end of lines, and with two byte characters. It returns TRUE
  417.  * if all of the characters were deleted, and FALSE if they were not
  418.  * (because dot ran into the end of the buffer. The "kflag" is TRUE if the
  419.  * text should be put in the kill buffer.
  420.  */
  421.  
  422. PASCAL NEAR ldelete(n, kflag)
  423.  
  424. long n;     /* # of chars to delete */
  425. int kflag;    /* put killed text in kill buffer flag */
  426.  
  427. {
  428.     register char    *cp1;
  429.     register char    *cp2;
  430.     register LINE    *dotp;
  431.     register int    doto;
  432.     register int    chunk;
  433.     register WINDOW *wp;
  434.     int cmark;        /* current mark */
  435.  
  436.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  437.         return(rdonly());    /* we are in read only mode    */
  438.  
  439.     while (n != 0) {
  440.  
  441. #if    DBCS
  442.         /* never start on a 2 byte char */
  443.         if (curwp->w_doto > 0 && is2byte(curwp->w_dotp->l_text,
  444.             &curwp->w_dotp->l_text[curwp->w_doto - 1])) {
  445.             curwp->w_doto--;
  446.             n++;
  447.         }
  448. #endif
  449.  
  450.         /* record the current point */
  451.         dotp = curwp->w_dotp;
  452.         doto = curwp->w_doto;
  453.  
  454.         /* can't delete past the end of the buffer */
  455.         if (dotp == curbp->b_linep)
  456.             return(FALSE);
  457.  
  458.         /* find out how many chars to delete on this line */
  459.         chunk = dotp->l_used-doto;    /* Size of chunk.    */
  460.         if (chunk > n)
  461.             chunk = n;
  462.  
  463.         /* if at the end of a line, merge with the next */
  464.         if (chunk == 0) {
  465.  
  466.             /* flag that we are making a hard change */
  467.             lchange(WFHARD);
  468.             if (ldelnewline() == FALSE
  469.                 || (kflag!=FALSE && kinsert('\r')==FALSE))
  470.                 return(FALSE);
  471.             --n;
  472.             continue;
  473.         }
  474.  
  475.         /* flag the fact we are changing the current line */
  476.         lchange(WFEDIT);
  477.  
  478.         /* find the limits of the kill */
  479.         cp1 = &dotp->l_text[doto];
  480.         cp2 = cp1 + chunk;
  481. #if    DBCS
  482.         if (is2byte(dotp->l_text, cp2 - 1)) {
  483.             ++chunk;
  484.             ++cp2;
  485.             ++n;
  486.         }
  487. #endif
  488.  
  489.         /* save the text to the kill buffer */
  490.         if (kflag != FALSE) {
  491.             while (cp1 != cp2) {
  492.                 if (kinsert(*cp1) == FALSE)
  493.                     return(FALSE);
  494.                 ++cp1;
  495.             }
  496.             cp1 = &dotp->l_text[doto];
  497.         }
  498.  
  499.         /* copy what is left of the line upward */
  500.         while (cp2 != &dotp->l_text[dotp->l_used])
  501.             *cp1++ = *cp2++;
  502.         dotp->l_used -= chunk;
  503.  
  504.         /* fix any other windows with the same text displayed */
  505.         wp = wheadp;
  506.         while (wp != NULL) {
  507.  
  508.             /* reset the dot if needed */
  509.             if (wp->w_dotp==dotp && wp->w_doto>=doto) {
  510.                 wp->w_doto -= chunk;
  511.                 if (wp->w_doto < doto)
  512.                     wp->w_doto = doto;
  513.             }
  514.  
  515.             /* reset any marks if needed */
  516.             for (cmark = 0; cmark < NMARKS; cmark++) {
  517.                 if (wp->w_markp[cmark]==dotp && wp->w_marko[cmark]>=doto) {
  518.                     wp->w_marko[cmark] -= chunk;
  519.                     if (wp->w_marko[cmark] < doto)
  520.                         wp->w_marko[cmark] = doto;
  521.                 }
  522.             }
  523.  
  524.             /* onward to the next window */
  525.             wp = wp->w_wndp;
  526.         }
  527.  
  528.         /* indicate we have deleted chunk characters */
  529.         n -= chunk;
  530.     }
  531.     return(TRUE);
  532. }
  533.  
  534. /* getctext:    grab and return a string with the text of
  535.         the current line
  536. */
  537.  
  538. char *PASCAL NEAR getctext()
  539.  
  540. {
  541.     register LINE *lp;    /* line to copy */
  542.     register int size;    /* length of line to return */
  543.     register char *sp;    /* string pointer into line */
  544.     register char *dp;    /* string pointer into returned line */
  545.     char rline[NSTRING];    /* line to return */
  546.  
  547.     /* find the contents of the current line and its length */
  548.     lp = curwp->w_dotp;
  549.     sp = lp->l_text;
  550.     size = lp->l_used;
  551.     if (size >= NSTRING)
  552.         size = NSTRING - 1;
  553.  
  554.     /* copy it across */
  555.     dp = rline;
  556.     while (size--)
  557.         *dp++ = *sp++;
  558.     *dp = 0;
  559.     return(rline);
  560. }
  561.  
  562. /* putctext:    replace the current line with the passed in text    */
  563.  
  564. PASCAL NEAR putctext(iline)
  565.  
  566. char *iline;    /* contents of new line */
  567.  
  568. {
  569.     register int status;
  570.  
  571.     /* delete the current line */
  572.     curwp->w_doto = 0;    /* starting at the beginning of the line */
  573.     if ((status = killtext(TRUE, 1)) != TRUE)
  574.         return(status);
  575.  
  576.     /* insert the new line */
  577.     if ((status = linstr(iline)) != TRUE)
  578.         return(status);
  579.     status = lnewline();
  580.     backline(TRUE, 1);
  581.     return(status);
  582. }
  583.  
  584. /*
  585.  * Delete a newline. Join the current line with the next line. If the next line
  586.  * is the magic header line always return TRUE; merging the last line with the
  587.  * header line can be thought of as always being a successful operation, even
  588.  * if nothing is done, and this makes the kill buffer work "right". Easy cases
  589.  * can be done by shuffling data around. Hard cases require that lines be moved
  590.  * about in memory. Return FALSE on error and TRUE if all looks ok. Called by
  591.  * "ldelete" only.
  592.  */
  593. PASCAL NEAR ldelnewline()
  594. {
  595.     register char    *cp1;
  596.     register char    *cp2;
  597.     register LINE    *lp1;
  598.     register LINE    *lp2;
  599.     register LINE    *lp3;
  600.     register WINDOW *wp;
  601.     SCREEN *scrp;        /* screen to fix pointers in */
  602.     int cmark;        /* current mark */
  603.  
  604.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  605.         return(rdonly());    /* we are in read only mode    */
  606.     lp1 = curwp->w_dotp;
  607.     lp2 = lp1->l_fp;
  608.     if (lp2 == curbp->b_linep) {        /* At the buffer end.    */
  609.         if (lp1->l_used == 0)        /* Blank line.        */
  610.             lfree(lp1);
  611.         return(TRUE);
  612.     }
  613.     if (lp2->l_used <= lp1->l_size-lp1->l_used) {
  614.         cp1 = &lp1->l_text[lp1->l_used];
  615.         cp2 = &lp2->l_text[0];
  616.         while (cp2 != &lp2->l_text[lp2->l_used])
  617.         *cp1++ = *cp2++;
  618.  
  619.         /* in all screens.... */
  620.         scrp = first_screen;
  621.         while (scrp) {
  622.  
  623.             wp = scrp->s_first_window;
  624.             while (wp != NULL) {
  625.                 if (wp->w_linep == lp2)
  626.                     wp->w_linep = lp1;
  627.                 if (wp->w_dotp == lp2) {
  628.                     wp->w_dotp  = lp1;
  629.                     wp->w_doto += lp1->l_used;
  630.                 }
  631.                 for (cmark = 0; cmark < NMARKS; cmark++) {
  632.                     if (wp->w_markp[cmark] == lp2) {
  633.                         wp->w_markp[cmark]  = lp1;
  634.                         wp->w_marko[cmark] += lp1->l_used;
  635.                     }
  636.                 }
  637.                 wp = wp->w_wndp;
  638.             }
  639.  
  640.             /* next screen! */
  641.             scrp = scrp->s_next_screen;
  642.         }
  643.  
  644.         lp1->l_used += lp2->l_used;
  645.         lp1->l_fp = lp2->l_fp;
  646.         lp2->l_fp->l_bp = lp1;
  647.         free((char *) lp2);
  648.         return(TRUE);
  649.     }
  650.     if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL)
  651.         return(FALSE);
  652.     cp1 = &lp1->l_text[0];
  653.     cp2 = &lp3->l_text[0];
  654.     while (cp1 != &lp1->l_text[lp1->l_used])
  655.         *cp2++ = *cp1++;
  656.     cp1 = &lp2->l_text[0];
  657.     while (cp1 != &lp2->l_text[lp2->l_used])
  658.         *cp2++ = *cp1++;
  659.     lp1->l_bp->l_fp = lp3;
  660.     lp3->l_fp = lp2->l_fp;
  661.     lp2->l_fp->l_bp = lp3;
  662.     lp3->l_bp = lp1->l_bp;
  663.  
  664.     /* in all screens.... */
  665.     scrp = first_screen;
  666.     while (scrp) {
  667.  
  668.         wp = scrp->s_first_window;
  669.         while (wp != NULL) {
  670.             if (wp->w_linep==lp1 || wp->w_linep==lp2)
  671.                 wp->w_linep = lp3;
  672.             if (wp->w_dotp == lp1)
  673.                 wp->w_dotp  = lp3;
  674.             else if (wp->w_dotp == lp2) {
  675.                 wp->w_dotp  = lp3;
  676.                 wp->w_doto += lp1->l_used;
  677.             }
  678.             for (cmark = 0; cmark < NMARKS; cmark++) {
  679.                 if (wp->w_markp[cmark] == lp1)
  680.                     wp->w_markp[cmark]  = lp3;
  681.                 else if (wp->w_markp[cmark] == lp2) {
  682.                     wp->w_markp[cmark]  = lp3;
  683.                     wp->w_marko[cmark] += lp1->l_used;
  684.                 }
  685.             }
  686.             wp = wp->w_wndp;
  687.         }
  688.  
  689.         /* next screen! */
  690.         scrp = scrp->s_next_screen;
  691.     }
  692.  
  693.     free((char *) lp1);
  694.     free((char *) lp2);
  695.     return(TRUE);
  696. }
  697.  
  698. /*    Add a new line to the end of the indicated buffer.
  699.     return FALSE if we run out of memory
  700.     note that this works on non-displayed buffers as well!
  701. */
  702.  
  703. #if    PROTO
  704. int PASCAL NEAR addline(BUFFER *bp, char *text)
  705. #else
  706. int PASCAL NEAR addline(bp, text)
  707.  
  708. BUFFER *bp;    /* buffer to add text to */
  709. char *text;    /* line to add */
  710. #endif
  711. {
  712.     register LINE    *lp;
  713.     register int    i;
  714.     register int    ntext;
  715.  
  716.     /* allocate the memory to hold the line */
  717.     ntext = strlen(text);
  718.     if ((lp=lalloc(ntext)) == NULL)
  719.         return(FALSE);
  720.  
  721.     /* copy the text into the new line */
  722.     for (i=0; i<ntext; ++i)
  723.         lputc(lp, i, text[i]);
  724.  
  725.     /* add the new line to the end of the buffer */
  726.     bp->b_linep->l_bp->l_fp = lp;
  727.     lp->l_bp = bp->b_linep->l_bp;
  728.     bp->b_linep->l_bp = lp;
  729.     lp->l_fp = bp->b_linep;
  730.  
  731.     /* if the point was at the end of the buffer,
  732.        move it to the beginning of the new line */
  733.     if (bp->b_dotp == bp->b_linep)
  734.         bp->b_dotp = lp;
  735.     return(TRUE);
  736. }
  737.  
  738. /*
  739.  * Delete all of the text saved in the kill buffer. Called by commands when a
  740.  * new kill context is being created. The kill buffer array is released, just
  741.  * in case the buffer has grown to immense size. No errors.
  742.  */
  743. PASCAL NEAR kdelete()
  744. {
  745.     KILL *kp;    /* ptr to scan kill buffer chunk list */
  746.  
  747.     if (kbufh != NULL) {
  748.  
  749.         /* first, delete all the chunks */
  750.         kbufp = kbufh;
  751.         while (kbufp != NULL) {
  752.             kp = kbufp->d_next;
  753.             free(kbufp);
  754.             kbufp = kp;
  755.         }
  756.  
  757.         /* and reset all the kill buffer pointers */
  758.         kbufh = kbufp = NULL;
  759.         kused = KBLOCK;             
  760.     }
  761. }
  762.  
  763. /*
  764.  * Insert a character to the kill buffer, allocating new chunks as needed.
  765.  * Return TRUE if all is well, and FALSE on errors.
  766.  */
  767.  
  768. #if    PROTO
  769. PASCAL NEAR kinsert(char c)
  770. #else
  771. PASCAL NEAR kinsert(c)
  772.  
  773. char c;        /* character to insert in the kill buffer */
  774. #endif
  775.  
  776. {
  777.     KILL *nchunk;    /* ptr to newly malloced chunk */
  778.  
  779.     /* check to see if we need a new chunk */
  780.     if (kused >= KBLOCK) {
  781.         if ((nchunk = (KILL *)malloc(sizeof(KILL))) == NULL)
  782.             return(FALSE);
  783.         if (kbufh == NULL)    /* set head ptr if first time */
  784.             kbufh = nchunk;
  785.         if (kbufp != NULL)    /* point the current to this new one */
  786.             kbufp->d_next = nchunk;
  787.         kbufp = nchunk;
  788.         kbufp->d_next = NULL;
  789.         kused = 0;
  790.     }
  791.  
  792.     /* and now insert the character */
  793.     kbufp->d_chunk[kused++] = c;
  794.     return(TRUE);
  795. }
  796.  
  797. /*
  798.  * Yank text back from the kill buffer. This is really easy. All of the work
  799.  * is done by the standard insert routines. All you do is run the loop, and
  800.  * check for errors. Bound to "C-Y".
  801.  */
  802. PASCAL NEAR yank(f, n)
  803.  
  804. int f,n;    /* prefix flag and argument */
  805.  
  806. {
  807.     register int c;
  808.     register int i;
  809.     register char *sp;    /* pointer into string to insert */
  810.     short int curoff;
  811.     LINE *curline;
  812.     KILL *kp;        /* pointer into kill buffer */
  813.  
  814.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  815.         return(rdonly());    /* we are in read only mode    */
  816.     if (n < 0)
  817.         return(FALSE);
  818.     /* make sure there is something to yank */
  819.     if (kbufh == NULL)
  820.         return(TRUE);        /* not an error, just nothing */
  821.  
  822.     /*
  823.      * Save the local pointers to hold global ".".
  824.      */
  825.     if (yanktype == SRBEGIN) {
  826.         /* Find the *previous* line, since the line we are on
  827.          * may disappear due to re-allocation.  This works even
  828.          * if we are on the first line of the file.
  829.          */
  830.         curline = lback(curwp->w_dotp);
  831.         curoff = curwp->w_doto;
  832.     }
  833.  
  834.     /* for each time.... */
  835.     while (n--) {
  836.         kp = kbufh;
  837.         while (kp != NULL) {
  838.             if (kp->d_next == NULL)
  839.                 i = kused;
  840.             else
  841.                 i = KBLOCK;
  842.             sp = kp->d_chunk;
  843.             while (i--) {
  844.                 if ((c = *sp++) == '\r') {
  845.                     if (lnewline() == FALSE)
  846.                         return(FALSE);
  847.                 } else {
  848.                     if (linsert(1, c) == FALSE)
  849.                         return(FALSE);
  850.                 }
  851.             }
  852.             kp = kp->d_next;
  853.         }
  854.     }
  855.  
  856.     /* If requested, set global "." back to the
  857.      * beginning of the yanked text.
  858.      */
  859.     if (yanktype == SRBEGIN) {
  860.         curwp->w_dotp = lforw(curline);
  861.         curwp->w_doto = curoff;
  862.     }
  863.     return(TRUE);
  864. }
  865.